﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using SimulationPresenter.Simulation;
using SimulationPresenter.Theater;
using SimulationPresenter.Theater.Enums;
using SimulationPresenter.Simulation.Enums;
using SimulationPresenter.UI;

namespace SimulationPresenter
{
    public partial class MainForm : Form
    {
        private Scene scene;

        private DisplayForm displayForm;

        public MainForm()
        {
            InitializeComponent();

            scene = new Scene().Setup();
            scene.BindAddActorAction(AppendActorToList);
            scene.BindRemoveActorAction(RemoveActorFromList);

            BindLoggers();
            BindKeyDownAction(Controls);
        }

        private void BindLoggers()
        {
            LogManager.ErrorLogger += new StringLogger(AppendError);
            LogManager.InfoLogger += new StringLogger(AppendInfo);
        }

        private void AppendError(string error)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(">>>> ERROR:");
            sb.AppendLine(error);
            sb.AppendLine("<<<<");
            LogOutput.Text += sb.ToString();
        }

        private void AppendInfo(string message)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("---- INFO:");
            sb.AppendLine(message);
            sb.AppendLine("----");
            LogOutput.Text += sb.ToString();
        }

        public void BindKeyDownAction(Control.ControlCollection controlCollection)
        {
            foreach (Control control in controlCollection)
            {
                control.KeyDown += control_KeyDown;
                BindKeyDownAction(control.Controls);
            }
        }

        void control_KeyDown(object sender, KeyEventArgs e)
        {
            if (null != displayForm)
            {
                displayForm.KeyDownProcess(e.KeyCode);
            }
        }

        /// <summary>
        /// Initializes DX base
        /// </summary>
        public void InitializeDX()
        {
            displayForm = new DisplayForm(this);
        }

        private void AddBoxButton_Click(object sender, EventArgs e)
        {
            scene.CreateAndAddDefaultBox();
        }

        private void AddConnectedButton_Click(object sender, EventArgs e)
        {
            scene.CreateAndAddDefaultBox(new Vector3(0, 20, 0));
            scene.CreateAndAddDefaultBox(new Vector3(-5, 22, 0));
            scene.JoinLastTwoActors();
        }

        public void DrawScene(Device device)
        {
            if (HoldSwitch.CheckState != CheckState.Checked)
            {
                //Update all elements on scene
                scene.UpdateScene(); 
            }

            //Draw scene into a device
            scene.DrawScene(device);

            ShowPositions();
        }

        private void ShowPositions()
        {
            var positions = new StringBuilder();
            var elements = ElementsList.Items.OfType<IActorPropertiesProvider>();
            elements = elements.OrderByDescending(x => x.ToString());

            foreach (object item in elements)
            {

                positions.AppendLine(string.Format((@"{0}: {1}"),
                                     item,
                                     (item as IActorPropertiesProvider).Position));
            }

            ElementPosition.Text = positions.ToString();
        }

        private void TabControl_VisibleChanged(object sender, EventArgs e)
        {
            if (this.Visible)
            {
                displayForm.Show();
            }
            else
            {
                displayForm.Hide();
            }
        }

        private void AppendActorToList(Actor actor)
        {
            ElementsList.Items.Add(actor);
        }

        private void RemoveActorFromList(Actor actor)
        {
            ElementsList.Items.Remove(actor);
        }

        private void ElementsList_SelectedIndexChanged(object sender, EventArgs e)
        {
            var actorTypes = new List<ActorType>();
            var densities = new List<float>();
            var visibility = new List<bool>();

            var selectedElements = ElementsList.SelectedItems.OfType<Actor>();
            var allActors = new Actor[ElementsList.Items.Count];
            ElementsList.Items.CopyTo(allActors, 0);
            var restOfActors = allActors.Where(x => !selectedElements.Contains(x));

            HighlightSelected(selectedElements);
            UnhighlightSelected(restOfActors);

            /*
             Information needed:
             * Visibility - true/false
             * Density - int
             */
            foreach (object item in selectedElements)
            {
                if (!(item is ITypeDiscoverableActor))
                {
                    continue;
                }
                if (!actorTypes.Contains((item as ITypeDiscoverableActor).ActorType))
                {
                    actorTypes.Add((item as ITypeDiscoverableActor).ActorType);
                }

                if (!(item is IActorPropertiesProvider))
                {
                    continue;
                }
                if (!visibility.Contains((item as IActorPropertiesProvider).Visible))
                {
                    visibility.Add((item as IActorPropertiesProvider).Visible);
                }

                if (!(item is IRidigActorPropertiesProvider))
                {
                    continue;
                }
                if (!densities.Contains((item as IRidigActorPropertiesProvider).Density))
                {
                    densities.Add((item as IRidigActorPropertiesProvider).Density);
                }

            }

            if (visibility.Count() < 1)
            {
                ElementVisibleCheckbox.Enabled = false;
            }
            else if (visibility.Count() > 1)
            {
                ElementVisibleCheckbox.Enabled = true;
                ElementVisibleCheckbox.CheckState = CheckState.Indeterminate;
            }
            else
            {
                ElementVisibleCheckbox.Enabled = true;
                ElementVisibleCheckbox.CheckState = visibility[0] ? CheckState.Checked : CheckState.Unchecked;
            }

            if (densities.Count() > 0)
            {
                ElementDensity.Value = (decimal)densities.Max();
                ElementDensity.Enabled = true;
            }
            else
            {
                ElementDensity.Enabled = false;
            }
        }

        private void HighlightSelected(IEnumerable<Actor> actors)
        {
            SetBoxColor(actors, Color.Yellow);
        }

        private void UnhighlightSelected(IEnumerable<Actor> actors)
        {
            SetBoxColor(actors, Color.DarkOrange);
        }

        private void SetBoxColor(IEnumerable<Actor> actors, Color color)
        {
            foreach (var actor in actors)
            {
                (actor as IDrawable).Highlight(color);
            }
        }

        private void ElementVisibleCheckbox_CheckedChanged(object sender, EventArgs e)
        {
            foreach (object item in ElementsList.SelectedItems)
            {
                if (!(item is IActorPropertiesProvider))
                {
                    continue;
                }
                (item as IActorPropertiesProvider).Visible = ElementVisibleCheckbox.CheckState == CheckState.Checked;
            }
        }

        private void ElementsJoin_Click(object sender, EventArgs e)
        {
            var selectedElements = ElementsList.SelectedItems.OfType<IActorPropertiesProvider>();

            if (selectedElements.Count() != 2)
            {
                throw new ArgumentOutOfRangeException("To join you should select ONLY two actors.");
            }
            var elementsList = selectedElements.ToList();

            scene.JoinActors(elementsList[0], elementsList[1]);
        }

        private void AddClothButton_Click(object sender, EventArgs e)
        {
            //scene.CreateAndAddCloth();
        }

        private void AddRidigMeshButton_Click(object sender, EventArgs e)
        {
            var openFileDialog = new OpenFileDialog();
            openFileDialog.ShowDialog();

            if (!string.IsNullOrEmpty(openFileDialog.FileName))
            {
                scene.CreateAndAddMesh(openFileDialog.FileName, displayForm.device);
            }
        }

        private void AddPoltergeistButton_Click(object sender, EventArgs e)
        {
            var openFileDialog = new OpenFileDialog();
            openFileDialog.ShowDialog();

            if (!string.IsNullOrEmpty(openFileDialog.FileName))
            {
                scene.CreatePoltergeistFromFile(openFileDialog.FileName, displayForm.device);
            }
        }

        private void SetButton_Click(object sender, EventArgs e)
        {
            if (-1 == SimulationTypeCombo.SelectedIndex)
            {
                MessageBox.Show("Please select simulation type", "Simulation type not selected");
                return;
            }
            switch (SimulationTypeCombo.SelectedItem.ToString().ToLower())
            {
                case "ridig":
                    SetSelectedActorsTo(SimulationType.Ridig);
                    break;
                case "dummy":
                    SetSelectedActorsTo(SimulationType.Dummy);
                    break;
                case "metal cloth":
                    SetSelectedActorsTo(SimulationType.MetalCloth);
                    break;
                default:
                    MessageBox.Show("Selected simulation type has not been implemented yet", "Simulation type not available");
                    break;
            }
        }

        private void SetSelectedActorsTo(SimulationType simulationType)
        {
            var selectedElements = ElementsList.SelectedItems.OfType<ISimulationTypeChangeableActor>();

            if (null == selectedElements ||
                    selectedElements.Count() < 1)
            {
                MessageBox.Show("Please select at least one actor to change its simulation type.", "Simulation type change");
                return;
            }

            foreach (var actor in selectedElements)
            {
                scene.SetActorToSimulationType(actor, simulationType);
            }
        }
    }
}
